#!/usr/bin/env python
import sys, os
import logging
import urllib, cgi

import oauth, BeautifulSoup

from plugins.douban.config import *
from plugins.douban.apikey import *

from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app

from google.appengine.api import urlfetch

class OAuthUser(db.Model):
    uid = db.IntegerProperty()    
    
    oauth_key = db.StringProperty()
    oauth_secret = db.StringProperty()
    
    name_id = db.StringProperty()
    name = db.StringProperty()
    location_id = db.StringProperty()
    location = db.StringProperty()
    
    @property
    def token(self):
        return oauth.OAuthToken(self.oauth_key, self.oauth_secret)

class OAuthClient(oauth.OAuthClient):
    def __init__(self, key=DOUBAN_API_KEY, secret=DOUBAN_SECRET, sign_method=oauth.OAuthSignatureMethod_HMAC_SHA1()):        
        oauth.OAuthClient.__init__(self, oauth.OAuthConsumer(key, secret), None)
        
        self.sign_method = sign_method
        
    def fetchRequestToken(self, url=DOUBAN_OAUTH_REQUEST_TOKEN):
        request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
                                                             http_url=url)
        request.sign_request(self.sign_method, self.consumer, None)
        
        logging.info("fetch request token: %s", request.to_header())
        
        result = urlfetch.fetch(url=url,
                                method=urlfetch.GET if request.http_method == "GET" else urlfetch.POST,
                                headers=request.to_header())
        
        if result.status_code == 200:
            return oauth.OAuthToken.from_string(result.content)
        
        raise RuntimeError("fail to fetch request token, status=%d" % result.status_code)
        
    def authorizeToken(self, token, callback, url=DOUBAN_OAUTH_AUTH_TOKEN):
        request = oauth.OAuthRequest.from_token_and_callback(token=token,
                                                             callback=callback,
                                                             http_url=url)
        return request.to_url()
        
    def fetchAccessToken(self, token, url=DOUBAN_OAUTH_ACCESS_TOKEN):
        request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
                                                             token=token,
                                                             http_url=url)
        request.sign_request(self.sign_method, self.consumer, token)
        
        logging.info("fetch access token: %s", request.to_header())
        
        result = urlfetch.fetch(url=url,
                                method=urlfetch.GET if request.http_method == "GET" else urlfetch.POST,
                                headers=request.to_header())
        
        if result.status_code == 200:
            params = cgi.parse_qs(result.content);            
            return int(params["douban_user_id"][0]), oauth.OAuthToken.from_string(result.content)
        
        raise RuntimeError("fail to fetch access token, status=%d" % result.status_code)
        
    def accessResource(self, token, url, method=urlfetch.GET, body=None):
        request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
                                                             token=token,
                                                             http_url=url)
        request.sign_request(self.sign_method, self.consumer, token)
        
        headers = request.to_header()
        
        if method in [urlfetch.GET, urlfetch.PUT]:
            headers['Content-Type'] = 'application/atom+xml; charset=utf-8'
            
        logging.info("access resource: %s", headers)
            
        result = urlfetch.fetch(url=url,
                                method=method,
                                payload=body,
                                headers=headers)
        
        if result.status_code == 200:
            return result.content
        
        raise RuntimeError("fail to access resource, status=%d, url=%s" % (result.status_code, url))
        
class Dispatcher(webapp.RequestHandler):
    def get(self):
        oauth_request_token = self.request.get("oauth_token", default_value=None)
        oauth_client = OAuthClient()
        
        if oauth_request_token:
            user = OAuthUser.all().filter("oauth_key =", oauth_request_token).get()
            
            if user:
                logging.info("found a request token: %s", user.token)
                
                user_id, oauth_access_token = oauth_client.fetchAccessToken(user.token)
                
                logging.info("got an access token for user %d: %s", user_id, oauth_access_token)
                
                xml = oauth_client.accessResource(oauth_access_token, DOUBAN_PEOPLE_ME)
                dom = BeautifulSoup.BeautifulSoup(xml)
                entry = dom.find('entry')
                
                user.uid = user_id
                user.oauth_key = oauth_access_token.key
                user.oauth_secret = oauth_access_token.secret
                
                user.name_id = unicode(entry.find('db:uid').string)
                user.name = unicode(entry.find('title').string)
                user.location_id = entry.find('db:location')["id"]
                user.location = unicode(entry.find('db:location').string)
                
                user.put()
                
                self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
                
                context = {
                    "name" : user.name,
                    "name_link" : entry.find("link", rel="self")["href"],
                    "location" : user.location,
                    "icon_link" : entry.find("link", rel="icon")["href"],
                }
                
                path = os.path.join(os.path.dirname(__file__), "welcome.htm")
                self.response.out.write(template.render(path, context))
            else:
                self.error(404)
                self.response.out.write('Opps, wrong token?')
        else:        
            import urlparse
            
            oauth_request_token = oauth_client.fetchRequestToken()
            
            logging.info("got a request token: %s", oauth_request_token)
            
            user = OAuthUser(oauth_key=oauth_request_token.key,
                             oauth_secret=oauth_request_token.secret)
            user.put()
            
            self.redirect(oauth_client.authorizeToken(oauth_request_token, self.request.url))         